﻿using FoundationLib.Ifc.DependencyInjection.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FoundationLib.Ifc.DependencyInjection
{
	/// <summary>
	/// Provides functionality for accessing <see cref="IIocContainer"/> implementations.
	/// </summary>
	public static class IocManager
	{
		/// <summary>
		/// The configuration
		/// </summary>
		private static IocConfigSection configuration;
		/// <summary>
		///	A dictionary containing container instances
		/// </summary>
		private static Dictionary<string, IIocContainer> containers;


		/// <summary>
		/// Initialises class variables.
		/// </summary>
		static IocManager()
		{
			containers = new Dictionary<string, IIocContainer>();
		}

		/// <summary>
		/// Makes sure that the configuration is loaded.
		/// </summary>
		private static void EnsureConfigurationLoaded()
		{
			if (configuration != null)
				return;

			configuration = IocConfigSection.FromConfiguration();

			if (configuration == null)
				throw new InvalidOperationException("Failed to load IOC container configuration.");
		}

		/// <summary>
		/// Makes sure that all containers defined in the configuration are created.
		/// </summary>
		private static void EnsureContainersCreated()
		{
			EnsureConfigurationLoaded();

			if (containers.Count > 0)
				return;

			foreach (IocConfigElement element in configuration.Containers)
			{
				Type implementation = Type.GetType(element.Implementation, true, true);
				IIocContainer container = Activator.CreateInstance(implementation) as IIocContainer;
				if (container == null)
					throw new InvalidCastException(String.Format("{0} does not implement the IIocContainer interface.",
						implementation.Name));

				container.Initialise();

				if (!containers.ContainsKey(element.Name))
					containers.Add(element.Name, container);
				else
					containers[element.Name] = container;
			}
		}

		/// <summary>
		/// Gets the <see cref="IIocContainer"/> implementation with the specified name.
		/// </summary>
		/// <param name="name">The container name</param>
		/// <returns>The container with the specified name</returns>
		public static IIocContainer GetContainer(string name)
		{
			EnsureContainersCreated();
			return containers[name];
		}

		/// <summary>
		/// Gets the default <see cref="IIocContainer"/> implementation.
		/// </summary>
		public static IIocContainer DefaultContainer
		{
			get
			{
				EnsureContainersCreated();
				IocConfigElement defaultElement = configuration.Containers.Default;
				if (defaultElement == null)
					return null;
				return containers[defaultElement.Name];
			}
		}
	}
}
